%{
/**
 * \file   sintaxis.y
 * \author Fabian Jesus Machado
 * \date   Septiembre de 2011
 * 
 *  Este archivo es parte del proyecto NPASM, un ensamblador para
 * microcontroladores PIC con sintaxis Intel, desarrollado inicialmente
 * por Fabian Machado (fabianjsm@gmail.com, twitter@fabianjsm).
 *  El codigo fuente esta diponible en el repositorio SVN de Google:
 *      http://npasm.googlecode.com/svn/trunk.
 * 
 * 
 */
#include "npasm.h"
#include "ts.h"
#include "entorno.h"
#include "emitir.h"
#include "auxiliar.h"
#include "cmd.h"
#include "directiva.h"

linea_asm ln;
struct_arg *arg;

template<class T>
void agmt(T val, size_t tipo)
{
	*arg = val;
	arg->tipo = tipo;
	arg = &ln.arg1;
}

static int dx;

%}

%union {
	class simbolo *sbl;
	unsigned long num;
}

%token RR
%token RL
%token PESO

%token DRTV_LOG
%token DRTV_DEFINE
%token DRTV_INCLUDE

%token CMD_SECTION
%token CMD_ALIGN
%token CMD_RESB
%token CMD_RESW
%token CMD_RESQ
%token CMD_DB
%token CMD_DW
%token CMD_DQ

%token <sbl>  W
%token <sbl>  IDENT
%token <sbl>  SECCION
%token <sbl>  ETIQUETA
%token <num>  NUM
%token <num>  CADENA
%token <sbl>  DEFINE_NUM
%token <sbl>  MKRETLW

%token <sbl>  INSTR_0
%token <sbl>  INSTR_1A
%token <sbl>  INSTR_2FK
%token <sbl>  INSTR_1FW
%token <sbl>  INSTR_2FW_FKW

%type <num>  dp_opcional
%type <num>  exp
%type <num>  exp_and
%type <num>  exp_xor
%type <num>  exp_rr
%type <num>  exp_rl
%type <num>  exp_suma
%type <num>  exp_mul
%type <num>  exp_unaria
%type <num>  exp_primaria

%%

fuente:
	| fuente linea      { Entorno->nl(); }
	| fuente error '\n' { throw(std::string("error sintactico")); }
;

linea:
	  {
		arg=&ln.arg0;
		ln.arg1.tipo=0;
	}
	etiqueta_opcional instruccion_o_directiva '\n'
;

instruccion_o_directiva:
	  instr_opcional
	  {
		emitir_instr(&ln);
	  }
	| directiva
	| comando
;

comando:
	  CMD_SECTION IDENT     { cmd_section($2);  }
	| CMD_SECTION SECCION   { cmd_section($2);  }
	| CMD_ALIGN exp         { cmd_align($2);    }
	| CMD_RESB exp          { cmd_resx($2, 1);  }
	| CMD_RESW exp          { cmd_resx($2, 2);  }
	| CMD_RESQ exp          { cmd_resx($2, 4);  }
	| CMD_DB { dx=1; } lst_d
	| CMD_DW { dx=2; } lst_d
	| CMD_DQ { dx=4; } lst_d
;

lst_d:
	  arg_lst_d
	| lst_d ',' arg_lst_d
;

arg_lst_d:
	  exp                   { cmd_dx($1, dx); }
	| CADENA                { cmd_dx($1, dx); }
;

directiva:
	  '%' DRTV_LOG lista_log         { drtv_log(LOG_FIN, 0); }
	| '%' DRTV_DEFINE IDENT exp      { drtv_define($3, $4);  }
	| '%' DRTV_DEFINE DEFINE_NUM exp { drtv_define($3, $4);  }
	| '%' DRTV_INCLUDE CADENA        { drtv_include($3);     }
;

lista_log:
	  exp_o_cadena
	| lista_log ',' exp_o_cadena
;

exp_o_cadena:
	  exp      { drtv_log(LOG_NUM, $1); }
	| CADENA   { drtv_log(LOG_STR, $1); }
;

etiqueta_opcional:
	| IDENT dp_opcional    { aceptar_etiqueta($1, $2);  }
	| ETIQUETA dp_opcional { aceptar_etiqueta($1, $2);  }
	| '.' IDENT            { aceptar_etiqueta($2, '.'); }
	| '.' ETIQUETA         { aceptar_etiqueta($2, '.'); }
;

dp_opcional:
			{ $$ = 0;  }
	| ':'	{ $$ = 1;  }
;

instr_opcional:
	                            { ln.i=0;                       }
	| INSTR_0                   { ln.i=$1; ln.t=INSTR_0;        }
	| INSTR_1A k                { ln.i=$1; ln.t=INSTR_1A;       }
	| INSTR_1FW fw              { ln.i=$1; ln.t=INSTR_1FW;      }
	| INSTR_2FW_FKW fkw ',' fkw { ln.i=$1; ln.t=INSTR_2FW_FKW;  }
	| INSTR_2FK f bit_op        { ln.i=$1; ln.t=INSTR_2FK;      }
	| MKRETLW lst_mkretlw       { ln.i=0;  }
;

lst_mkretlw:
	  parte_lst_retlw
	| lst_mkretlw ',' parte_lst_retlw
;

parte_lst_retlw:
	CADENA    { emitir_mkretlw($1, CADENA); }
	| exp     { emitir_mkretlw($1, NUM);    }
;

bit_op:
	| '.' k
;

k:
	exp           { agmt($1, ARG_NUM); }
;

fkw:
	  exp         { agmt($1, ARG_NUM); }
	| fw
;

fw:
	  W           { agmt($1, ARG_W);   }
	| f
;

f:
	'[' exp ']'   { agmt($2, ARG_F);   }
;

exp:
	  exp_and
	| exp '|' exp_and     { $$ = $1 | $3; }
;

exp_and:
	  exp_xor
	| exp_and '&' exp_xor { $$ = $1 & $3; }
;

exp_xor:
	  exp_rr
	| exp_xor '^' exp_rr  { $$ = $1 ^ $3; }
;

exp_rr:
	  exp_rl
	| exp_rr RR exp_rl    { $$ = $1 >> $3; }
;

exp_rl:
	  exp_suma
	| exp_rl RL exp_suma  { $$ = $1 << $3; }
;

exp_suma:
	  exp_mul
	| exp_suma '+' exp_mul   { $$ = $1 + $3; }
	| exp_suma '-' exp_mul   { $$ = $1 - $3; }
;


exp_mul:
	  exp_unaria
	| exp_mul '*' exp_unaria { $$ = $1 * $3; }
	| exp_mul '/' exp_unaria { $$ = emitir_division($1, $3); }
	| exp_mul '%' exp_unaria { $$ = $1 % $3; }
;

exp_unaria:
	  exp_primaria
	| '~' exp_unaria { $$ = ~$2; }
	| '!' exp_unaria { $$ = !$2; }
	| '-' exp_unaria { $$ = -$2; }
;

exp_primaria:
	  NUM
	| ETIQUETA  { $$ = $1->ToUInt(); }
	| IDENT
	  {
		$$ = 0;
		if(Entorno->GetPaso() != 1) {
			const char *s = $1->GetNombre().c_str();
			throw(mkstring("no se ha definido '%s'", s));
		}
	  }
	| '(' exp ')'  { $$ = $2;}
	| PESO         { $$ = Entorno->GetP(); }
	| DEFINE_NUM   { $$ = $1->ToUInt();    }
;

%%
